import shlex
import subprocess
import sys
import pexpect
from lxml import etree


class Utils:
    @staticmethod
    def exec_command_in_shell(commands: str):
        if sys.version_info >= (3, 7):
            process = subprocess.Popen(commands,
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       text=True)
            stdout, stderr = process.communicate()
            return process.returncode, stdout, stderr
        else:
            process = subprocess.Popen(commands,
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       universal_newlines=True)
            stdout, stderr = process.communicate()
            return process.returncode, stdout, stderr

    @staticmethod
    def exec_command_in_shell_with_input(commands, input_data: str):
        if sys.version_info >= (3, 7):
            process = subprocess.Popen(
            commands,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
            )
            stdout, stderr = process.communicate(input=input_data)
            return process.returncode, stdout.strip(), stderr.strip()
        else:
            process = subprocess.Popen(
            commands,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True
            )
            stdout, stderr = process.communicate(input=input_data)
            return process.returncode, stdout.strip(), stderr.strip()

    @staticmethod
    def exec_command(commands: str):
        try:
            if sys.version_info >= (3, 7):
                process = subprocess.Popen(commands,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.PIPE,
                                           text=True)
                stdout, stderr = process.communicate()
                return process.returncode, stdout, stderr
            else:
                process = subprocess.Popen(commands,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.PIPE,
                                           universal_newlines=True)
                stdout, stderr = process.communicate()
                return process.returncode, stdout, stderr
        except Exception as e:
            print(e)
        return -1, '', ''



    @staticmethod
    def query_user_group(group_name: str):
        command = 'cat /etc/group | grep ' + group_name
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code == 0:
            return stdout.find(group_name) >= 0
        else:
            return False

    @staticmethod
    def query_user(user_name: str):
        command = 'cat /etc/passwd | grep ' + user_name
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code == 0:
            return stdout.find(user_name) >= 0
        else:
            return False

    @staticmethod
    def get_current_user():
        command = 'whoami'
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code == 0:
            return True, stdout.strip()
        else:
            return False, stderr.strip()

    @staticmethod
    def create_user(group_name: str, user_name: str):
        command = 'useradd -g {} {}'.format(group_name, user_name)
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code == 0:
            return True, stdout.strip()
        else:
            return False, stderr.strip()

    @staticmethod
    def set_user_passwd(user_name, passwd):
        command = ['passwd', user_name]
        intput_data = '{}\n{}\n'.format(passwd, passwd)
        code, stdout, stderr = Utils.exec_command_in_shell_with_input(command, intput_data)
        if code == 0:
            return True, stdout.strip()
        else:
            return False, stderr.strip()

    @staticmethod
    def create_user_group(group_name: str):
        command = 'groupadd {}'.format(group_name)
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code == 0:
            return True, stdout.strip()
        else:
            return False, stderr.strip()

    @staticmethod
    def check_firewalled_status():
        command = 'systemctl status firewalld.service'
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code == 0:
            if stdout.find('running') >= 0:
                status ='running'
            else:
                status = 'not running'
            return True, status
        return False, stderr.strip()

    @staticmethod
    def close_firewalled():
        command = 'systemctl stop firewalld.service'
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        command = 'systemctl disable firewalld.service'
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def get_installed_lib():
        ret_list = []
        command = 'rpm -qa'
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, ret_list, stderr.strip()
        if len(stdout) > 0:
            lines = stdout.splitlines()
            for line in lines:
                ret_list.append(line)
        return True, ret_list, "success"

    @staticmethod
    def install_lib(lib_list):
        command = 'sudo yum install -y '
        for lib in lib_list:
            command += lib
            command += ' '
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, "success"

    @staticmethod
    def get_selinux_satus():
        command = 'getenforce'
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def temporary_close_selinux():
        command = 'setenforce 0'
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def adjust_system_parameters(system_parameters):
        sysctl_directory = '/etc/sysctl.d'
        sysctl_file = 'issedb_sysctl.conf'
        sysctl_file_path = sysctl_directory + '/' + sysctl_file

        ret, msg = Utils.check_file_exist(sysctl_file, sysctl_directory)
        if ret is True:
            return True, "success"
        try:
            with open(sysctl_file_path, 'w') as file:
                for key, value in system_parameters.items():
                    file.write(f"{key} = {value}\n")

            command = 'sysctl -p {}'.format(sysctl_file_path)
            code, stdout, stderr = Utils.exec_command_in_shell(command)
            if code is None or code != 0:
                return False, stderr.strip()

            return True, stdout.strip()
        except Exception as e:
            return False, str(e)
        # for key, value in system_parameters.items():
        #     command = 'sysctl {}=\'{}\''.format(key, value)
        #     code, stdout, stderr = Utils.exec_command_in_shell(command)
        #     if code is None or code != 0:
        #         return False, stderr.strip()
        # command = 'sysctl -p'
        # code, stdout, stderr = Utils.exec_command_in_shell(command)
        # if code is None or code != 0:
        #     return False, stderr.strip()


    @staticmethod
    def check_ipc_parameters(conf_path):
        command = 'cat {} | grep RemoveIPC'.format(conf_path)
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if len(stderr) != 0:
            return False, stderr.strip()
        lines = stdout.splitlines()
        for line in lines:
            line = line.strip()
            if line.find('RemoveIPC') >= 0:
                if line.startswith('#'):
                    continue
                else:
                    return True, 'found'
        return True, 'not found'

    @staticmethod
    def set_ipc_parameters(conf_path):
        command = 'echo "RemoveIPC=no" >> {}'.format(conf_path)
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def active_ipc_parameters(params):
        command = 'systemctl {}'.format(params)
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def print_title(title):
        print('-' * 40 + title + '-' * 40)

    @staticmethod
    def print_config(config):
        for key, value in config.items():
            print('- {}: {}'.format(key, value))

    @staticmethod
    def set_hostname(hostname, nodes):
        print('set hostname')
        command = 'hostnamectl set-hostname {}'.format(hostname)
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        command = 'echo -n > /etc/hosts'
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        for node in nodes:
            for key, value in node.items():
                command = 'echo "{}    {}" >> /etc/hosts'.format(value, key)
                code, stdout, stderr = Utils.exec_command_in_shell(command)
                if code is None or code != 0:
                    return False, stderr.strip()
        # command = 'systemctl restart network'
        # code, stdout, stderr = Utils.exec_command_in_shell(command)
        # if code is None or code != 0:
        #     return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def mkdir_chomd(user, grp, path):
        print('mkdir_chomd')
        command = 'mkdir -p {}'.format(path)
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return Utils.exec_chomd(user, grp, path)

    @staticmethod
    def exec_chomd(user, grp, path):
        print('chmod:' + path)
        chmod_command = ["chmod", "-R", "775", path]
        # command = 'chmod 755 -R {}'.format(path)
        code, stdout, stderr = Utils.exec_command(chmod_command)
        if code is None or code != 0:
            return False, stderr.strip()
        chown_command = ["chown", "-R", "{}:{}".format(user, grp), path]
        # command = 'chown -R {}:{} {}'.format(user, grp, path)
        code, stdout, stderr = Utils.exec_command(chown_command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def append_limits_conf(limits_conf):
        for limit in limits_conf:
            command = 'grep -q "{}" /etc/security/limits.conf'.format(limit)
            code, stdout, stderr = Utils.exec_command_in_shell(command)
            if code == 0:
                continue
            command = 'echo "{}" >> /etc/security/limits.conf'.format(limit)
            code, stdout, stderr = Utils.exec_command_in_shell(command)
            if code is None or code != 0:
                return False, stderr.strip()
        return True, 'success'

    @staticmethod
    def check_file_exist(file_name, path):
        if path is None:
            path = '.'
        code, stdout, stderr = Utils.exec_command_in_shell('find {} | grep {}'.format(path, file_name))
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def unzip_package(package_name, package_om, path):
        command = 'tar -zxvf {}/{} -C {}'.format(path, package_name, path)
        print("command:{}".format(command))
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        ret, msg = Utils.check_file_exist(package_om, path)
        if ret is False:
            return ret, msg
        command = 'tar -zxvf {}/{} -C {}'.format(path, package_om, path)
        print("command:{}".format(command))
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

    @staticmethod
    def create_xml_config(xml_tree, config, install_path):
        print('create_xml_config')
        ret, msg = Utils.check_file_exist('cluster_config.xml', install_path)
        if ret is True:
            return ret, msg
        else:
            print('cluster_config.xml file not exist, begin create')
        nodes = config['nodes']
        node_names = ''
        back_ip1s = ''

        for index, node in enumerate(nodes):
            for key, value in node.items():
                if index == 0:
                    node_names = key
                    back_ip1s = value
                else:
                    node_names = node_names + ',' + key
                    back_ip1s = back_ip1s + ',' + value



        root = xml_tree.getroot()
        # Find all PARAM elements within the CLUSTER element
        cluster_params = root.xpath('.//CLUSTER/PARAM')

        # Display each PARAM element
        for param in cluster_params:
            param_name = param.attrib['name']
            if param_name == 'clusterName':
                param.attrib['value'] = config['clusterName']
            if param_name == 'nodeNames':
                param.attrib['value'] = node_names
            if param_name == 'backIp1s':
                param.attrib['value'] = back_ip1s
            print("Parameter Name: {}, Value: {}".format(param_name, param.attrib['value']))

        devicelist = root.xpath('.//DEVICELIST')
        if len(nodes) > 2:
            for index, node in enumerate(nodes):
                if index > 1:
                    device =  etree.Element('DEVICE', attrib={'sn': '100000' + chr(index + 1)})
                    p1 = etree.Element('PARAM', attrib={'name': 'op_slave'})
                    p2 = etree.Element('PARAM', attrib={'azName': 'AZ' + chr(index + 1)})
                    p3 = etree.Element('PARAM', attrib={'azPriority': chr(index + 1)})
                    p4 = etree.Element('PARAM', attrib={'backIp1': '127.0.0.1'})
                    p5 = etree.Element('PARAM', attrib={'sshIp1': '127.0.0.1'})
                    p6 = etree.Element('PARAM', attrib={'cmDir': '/opt/issedb/cm'})
                    device.append(p1)
                    device.append(p2)
                    device.append(p3)
                    device.append(p4)
                    device.append(p5)
                    device.append(p6)
                    devicelist.append(device)

        devices = root.xpath('.//DEVICELIST/DEVICE')
        for index, device in enumerate(devices):
            device_params = device.xpath('.//PARAM')
            for param in device_params:
                param_name = param.attrib['name']
                if param_name == 'name':
                    for key, value in nodes[index].items():
                        param.attrib['value'] = key
                if param_name == 'backIp1' or param_name == 'sshIp1':
                    for key, value in nodes[index].items():
                        param.attrib['value'] = value

                if index == 0:
                    if param_name == 'cmServerListenIp1':
                        param.attrib['value'] = back_ip1s
                    if param_name == 'cmServerRelation':
                        param.attrib['value'] = node_names
                    if param_name == 'dataNode1':
                        param.attrib['value'] = config['dataNode1']
                    if param_name == 'dataNode1_syncNum':
                        param.attrib['value'] = config['dataNode1_syncNum']

        tree = etree.ElementTree(root)  # 如果你从现有文件载入并修改了树结构，这一行不需要
        tree.write(install_path + '/cluster_config.xml', pretty_print=True, xml_declaration=True, encoding='UTF-8')
        return True, 'success'

    @staticmethod
    def exec_gs_preinstall(path, node_name):
        command = 'python3'
        script_path = '{}/script/gs_preinstall'.format(path)
        arguments = ['-U', 'omm', '-G', 'dbgrp', '-X', '{}/cluster_config.xml'.format(path)]

        # 将命令和参数组合在一起
        cmd = [command, script_path] + arguments
        print('cmd : {}'.format(cmd))

        code, stdout, stderr = Utils.exec_command_in_shell_with_input(cmd, 'yes\n')
        if code is None or code != 0:
            checkos_path = '{}/script/gs_checkos'.format(path)
            checkos_arg = ['-i', 'A', '-h', node_name, '--detail']
            help_cmd = [command, checkos_path] + checkos_arg
            Utils.exec_command_in_shell_with_input(help_cmd, '')
            return False, stderr.strip()
        return True, stdout.strip()


    @staticmethod
    def exec_install_database1(config):
        path = config['installPath']
        db_pwd = config['dbPasswd']
        command = 'python3 '
        script_path = ' {}/script/gs_install'.format(path)
        arguments = ' -X' + ' {}/cluster_config.xml'.format(path) + ' --gsinit-parameter=\"--encoding=UTF8\"' + ' --dn-guc=\"max_connections=2000\"' + ' --dn-guc=\"max_process_memory={}\"'.format(config['max_process_memory']) + ' --dn-guc=\"shared_buffers={}\"'.format(config['shared_buffers']) + ' --dn-guc=\"bulk_write_ring_size={}\"'.format(config['bulk_write_ring_size']) + ' --dn-guc=\"cstore_buffers={}\"'.format(config['cstore_buffers'])
        flag = 0
        cmd = command + script_path + arguments
        print(cmd)
        execinstallscp = pexpect.spawn(cmd, timeout=600)

        execinstallscp.expect("enter password for database", timeout=600)
        execinstallscp.sendline(db_pwd)

        execinstallscp.expect("Please repeat for database", timeout=600)
        execinstallscp.sendline(db_pwd)
        # 等待执行完成
        print("已经发送完所有指令，等待安装完毕...")
        
        execinstallscp.expect("end deploy..", timeout=600)  # 根据实际情况修改关键词
        print("安装已完成")
        execinstallscp.close()
        print("安装脚本已执行完毕")
        return True, "success"

    @staticmethod
    def exec_install_database(config):
        path = config['installPath']
        db_pwd = config['dbPasswd']
        command = 'python3 '
        script_path = ' {}/script/gs_install'.format(path)
        arguments = ' -X' + ' {}/cluster_config.xml'.format(path) + ' --gsinit-parameter=\"--encoding=UTF8\"' + ' --dn-guc=\"max_connections=2000\"' + ' --dn-guc=\"max_process_memory={}\"'.format(config['max_process_memory']) + ' --dn-guc=\"shared_buffers={}\"'.format(config['shared_buffers']) + ' --dn-guc=\"bulk_write_ring_size={}\"'.format(config['bulk_write_ring_size']) + ' --dn-guc=\"cstore_buffers={}\"'.format(config['cstore_buffers'])
    

        try:
            cmd = command + script_path + arguments
            print(cmd)
            execinstallscp = pexpect.spawn(cmd, timeout=600)

            execinstallscp.expect("enter password for database", timeout=600)
            execinstallscp.sendline(db_pwd)

            execinstallscp.expect("Please repeat for database", timeout=600)
            execinstallscp.sendline(db_pwd)

            # 等待执行完成
            print("已经发送完所有指令，等待安装完毕...")

            # 关键步骤：如果成功匹配到"end deploy.."，标记为成功
            execinstallscp.expect("end deploy..", timeout=600)
            print("安装已完成")
            
            return True, "Successful install"
               
        except pexpect.TIMEOUT:
            print("错误：执行超时，安装可能失败")
            return False, "install failure, Timeout"
        except pexpect.EOF:
            print("错误：子进程意外关闭")
            return False, "Child process unexpectedly closed"
        except Exception as e:
            print(f"错误：发生异常 - {str(e)}")
            return False, "abnormal program termination"
        finally:
            # 确保关闭子进程
            if 'execinstallscp' in locals() and execinstallscp.isalive():
                execinstallscp.close()
            print("安装脚本已执行完毕")
            return True, "The program has ended normally"  # 返回标记结果

    @staticmethod
    def mv_file( filename,  dst):
        command = 'cp {} {}'.format(filename, dst)
        code, stdout, stderr = Utils.exec_command_in_shell(command)
        if code is None or code != 0:
            return False, stderr.strip()
        return True, stdout.strip()

